﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using log4net;
using System.Data.Objects;
using System.Text;

namespace AccountingModel.BLO
{
    public class AccountManager
    {
        private static readonly ILog Logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        public static AccountManager GetInstance()
        {
            return new AccountManager();
        }

        public int AddAccount(string accountName)
        {
            using (AccountingDBEntities db = Schema.ModelFactory.getInstance())
            {
                var query = from a in db.Accounts
                            where a.AccountName == accountName && a.Deleted != true
                            select a;
                if (query.Count() > 0)
                {
                    return -1;
                }
                Account acc = new Account() { AccountName = accountName };
                db.AddToAccounts(acc);
                try
                {
                    db.SaveChanges();
                }
                catch (Exception ex)
                {
                    if (Logger.IsErrorEnabled)
                    {
                        Logger.Error("Fail to persist change into DB.", ex);
                    }
                    return -1;
                }
                return acc.Id;
            }
        }

        public string RemoveAccount(int Id)
        {
            using (AccountingDBEntities db = Schema.ModelFactory.getInstance())
            {
                var query = from p in db.Accounts
                            where p.Id == Id
                            select p;
                Account acc = query.SingleOrDefault();
                if (acc != null)
                {
                    if (acc.TransactionCredits.Count + acc.TransactionDebits.Count > 0)
                    {
                        if (acc.TransactionCredits.Sum(i => i.Amount) == acc.TransactionDebits.Sum(i => i.Amount))
                        {
                            acc.Deleted = true;
                        }
                        else
                        {
                            return "Cannot delete account. Its balance is not 0.";
                        }
                    }
                    else
                    {
                        db.DeleteObject(acc);
                    }
                }
                else
                {
                    return "Account Id not found.";
                }
                try
                {
                    db.SaveChanges();
                }
                catch (Exception ex)
                {
                    if (Logger.IsErrorEnabled)
                    {
                        Logger.Error("Fail to persist change into DB.", ex);
                    }
                    return "Server internal error. Fail to persist into DB.";
                }
                return null;
            }
        }

        public string EditAccount(DTO.DTOAccount account)
        {
            using (AccountingDBEntities db = Schema.ModelFactory.getInstance())
            {
                var query = from p in db.Accounts
                            where p.Id == account.Id && (p.Deleted == false || p.Deleted == null)
                            select p;
                Account acc = query.SingleOrDefault();
                if (acc != null)
                {
                    acc.AccountName = account.AccountName;
                }
                else
                {
                    return "Account Id not found.";
                }
                try
                {
                    db.SaveChanges();
                }
                catch (Exception ex)
                {
                    if (Logger.IsErrorEnabled)
                    {
                        Logger.Error("Fail to persist change into DB.", ex);
                    }
                    return "Server internal error. Fail to persist into DB.";
                }
                return null;
            }
        }

        public List<DTO.DTOAccount> GetAccountList()
        {
            using (AccountingDBEntities db = Schema.ModelFactory.getInstance())
            {
                var query = from acc in db.Accounts
                            where acc.Deleted == null || acc.Deleted == false
                            orderby acc.AccountName
                            select new DTO.DTOAccount()
                            {
                                Id = acc.Id,
                                AccountName = acc.AccountName
                            };
                List<DTO.DTOAccount> result = query.ToList();
                return result;
            }
        }

        public decimal GetBalance(IList<int> AccountIds, DateTime atTime)
        {
            StringBuilder builder = new StringBuilder(",");
            if (AccountIds == null)
            {
                return 0;
            }
            foreach (var item in AccountIds)
            {
                builder.Append(item).Append(",");
            }
            using (AccountingDBEntities db = Schema.ModelFactory.getInstance())
            {
                decimal? result = db.AccountBalance(builder.ToString(), atTime.Date).SingleOrDefault();
                if (result == null)
                {
                    return 0;
                }
                else
                {
                    return result.Value;
                }
            }
        }

        public IList<DTO.DTOAccountActivity> GetAccountActivities(IList<int> AccountIds, DateTime fromDate, DateTime toDate)
        {
            StringBuilder builder = new StringBuilder(",");
            if (AccountIds == null)
            {
                return null;
            }
            foreach (var item in AccountIds)
            {
                builder.Append(item).Append(",");
            }
            IList<DTO.DTOAccountActivity> result = new List<DTO.DTOAccountActivity>();
            using (AccountingDBEntities db = Schema.ModelFactory.getInstance())
            {
                var query = db.GetAccountActivities(builder.ToString(), fromDate.Date, toDate.Date);
                foreach (var item in query)
                {
                    result.Add(new DTO.DTOAccountActivity()
                    {
                        TransactionId = item.TransactionId,
                        Date = item.Date,
                        Explaination = item.Explanation,
                        Debit = item.Debits ?? 0,
                        Credit = item.Credits ?? 0
                    });
                }
            }
            return result;
        }

        public IList<DTO.DTOAccountSummary> GetAccountSummaries(IList<int> AccountIds, IList<DateTime> points)
        {
            if (AccountIds == null || points == null)
            {
                return null;
            }
            IList<DTO.DTOAccountSummary> result = new List<DTO.DTOAccountSummary>();
            using (AccountingDBEntities db = new AccountingDBEntities())
            {
                foreach (var acc in AccountIds)
                {
                    DTO.DTOAccountSummary summary = new DTO.DTOAccountSummary();
                    summary.AccountId = acc;
                    summary.Balances = new List<DTO.DTOBalance>();
                    foreach (var point in points)
                    {
                        decimal? balance = db.AccountBalance(String.Format(",{0},", acc), point.AddDays(-1)).SingleOrDefault();
                        summary.Balances.Add(new DTO.DTOBalance()
                        {
                            Time = point,
                            Balance = balance ?? 0
                        });
                    }
                    result.Add(summary);
                }
            }
            return result;
        }
    }
}